#include<iostream>
using namespace std;
#include<unistd.h>
#include<cassert>
#include<string>
#include<vector>
#include<cstring>
#include<functional>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>


#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)



#define PROCESS_NUM 5


////////////////////////////////子进程要完成的某个任务/////////////
//使用函数指针 类型
typedef void (*func_t)();  //func_t 是函数指针的名称，返回值为void

void downLoadTask(){
    cout<<getpid()<<" : 下载任务"<<endl;
    sleep(1);
}
void ioTask(){
    cout<<getpid()<<" : IO任务"<<endl;
    sleep(1);
}
void flushTask(){
    cout<<getpid()<<" : 刷新任务"<<endl;
    sleep(1);
}

void loadTaskFunc(vector<func_t>* out){
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}

//就是写一个父进程的类来存放子进程的信息（包括子进程的id，还有父进程的文件描述数）
class subEp{
    public:
    //构造函数
    subEp(pid_t subId,int writefd)
    :_subId(subId),_writeFd(writefd){
        char namebuffer[1024];//子进程的名称
        snprintf(namebuffer,sizeof namebuffer,"process-%d[pid(%d)-fd(%d)]",num++,_subId,_writeFd);
        _name = namebuffer;
    }


    public:
    static int num;
    string _name;//要接收的任务的名字
    pid_t _subId;//发送任务进程的pid
    int _writeFd;//发送任务的进程的打开的写端的文件描述数

};
int subEp::num = 0;

//发送任务给子进程，就是write到父进程保存的fds中，然后写入的是一个函数指针，
void sendTask(const subEp& process,int tasknum){
    cout<<"send task num: "<<tasknum<<"send to -> "<<process._name<<endl;
    int n = write(process._writeFd, &tasknum ,sizeof(tasknum));
    //write的返回值是 

    (void)n;

}


//接收任务通过fds文件描述数
//因为子进程要读信息，是使用read方法进行的，所以才要传文件描述数
int recvTask(int readFd){
    int code = 0;
    ssize_t s = read(readFd,&code,sizeof code);
    //assert(s == sizeof(int));
    if(s == 4) return code;
    else if(s <= 0) return -1;
    else return 0;
}

//把整个创建子进程的过程封装为一个函数
void createSubProcess(vector<subEp>* subs,vector<func_t>& funcmap){
    for(int i = 0;i<PROCESS_NUM;i++){
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;


        pid_t id = fork();
        if(id == 0){
            //子进程，进行处理任务
            //子进程用于读取数据
            close(fds[1]);
            while(true){
                //1:获取命令码，如果没有发送，子进程应该阻塞
                int commandCode = recvTask(fds[0]);
                //2:完成任务，如果读到的code>0，也就是函数指针数组的下标来决定要执行哪一个任务
                if(commandCode >= 0 && commandCode < funcmap.size()){
                    funcmap[commandCode]();//函数指针数组来调用函数---》回调函数

                }else if(commandCode == -1){
                    //cout<<"sub recv code errorl"<<endl;
                    break;
                }

            }

            exit(0);
        }
        //父进程用来写数据，所以读端要关闭
        close(fds[0]);
        //父进程保存每个子进程的信息
        subEp sub(id,fds[1]); //这里的id是子进程的，fds【1】是父进程的
        subs->push_back(sub); //插入到vector中，方便管理
    }

}


void loadBlanceContrl(const vector<subEp>& subs , const vector<func_t>& funcMap,int count){

    int processnum = subs.size();//创建出来的子进程任务的数量
    int tasknum = funcMap.size();
    //bool quit = false;
    bool forever = (count == 0 ? true : false);

    while(true){
        //1,选择一个子进程--》vector<subEp> ->index 随机数
        int subIdx = rand() % processnum;
        //2:选择一个任务--》vector<func_t> ->index
        int TaskIdx = rand() % tasknum;
        //3:发送任务给选择的进程
        sendTask(subs[subIdx],TaskIdx);
        sleep(1);

        if(!forever){
            count--;
            if(count == 0) break;
        }

        //write quit -> read 0
        for(int i = 0;i < processnum; i++){
            close(subs[i]._writeFd);
        }
    }
}


//等待回收子进程
void waitProcess(vector<subEp> process){
    int processnum = process.size();
    for(int i = 0; i < processnum;i++){
        waitpid(process[i]._subId,nullptr,0);
        cout<<"wait sub process success ..."<<process[i]._subId<<endl;
    }
}

//基于匿名管道的进程池的设计
int main(){
    MakeSeed();
    //1:建立子进程并建立子进程通信的管道
    //[子进程id,wfd]是一个键值对
    //子进程要执行的任务也要管理起来，也就是父进程向子进程发送的命令
    vector<func_t> funcMap;//这里相当于将一批函数指针存放到一个数组中，就是函数指针数组，当我们通过数组下标去调用这些函数的方式，称为回调函数
    loadTaskFunc(&funcMap);

    //子进程的任务信息要管理起来。
    vector<subEp> subs;//用vector来存放创建出来的子进程的任务信息
    //1：创建子进程的任务
    createSubProcess(&subs,funcMap);

     //2：走到这里就是父亲进程
     //父进程要发任务给子进程
    int taskCnt = 20;
    loadBlanceContrl(subs,funcMap,taskCnt);

    
    //3：回收子进程
    waitProcess(subs);
    




    return 0;
}
